home *** CD-ROM | disk | FTP | other *** search
/ Amiga Plus 2002 #11 / Amiga Plus CD - 2002 - No. 11.iso / Tools / Development / ncurses-5.3 / ncurses / tty / lib_mvcur.c < prev    next >
Encoding:
C/C++ Source or Header  |  2002-10-27  |  36.7 KB  |  1,202 lines

  1. /****************************************************************************
  2.  * Copyright (c) 1998-2001,2002 Free Software Foundation, Inc.              *
  3.  *                                                                          *
  4.  * Permission is hereby granted, free of charge, to any person obtaining a  *
  5.  * copy of this software and associated documentation files (the            *
  6.  * "Software"), to deal in the Software without restriction, including      *
  7.  * without limitation the rights to use, copy, modify, merge, publish,      *
  8.  * distribute, distribute with modifications, sublicense, and/or sell       *
  9.  * copies of the Software, and to permit persons to whom the Software is    *
  10.  * furnished to do so, subject to the following conditions:                 *
  11.  *                                                                          *
  12.  * The above copyright notice and this permission notice shall be included  *
  13.  * in all copies or substantial portions of the Software.                   *
  14.  *                                                                          *
  15.  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS  *
  16.  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF               *
  17.  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.   *
  18.  * IN NO EVENT SHALL THE ABOVE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,   *
  19.  * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR    *
  20.  * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR    *
  21.  * THE USE OR OTHER DEALINGS IN THE SOFTWARE.                               *
  22.  *                                                                          *
  23.  * Except as contained in this notice, the name(s) of the above copyright   *
  24.  * holders shall not be used in advertising or otherwise to promote the     *
  25.  * sale, use or other dealings in this Software without prior written       *
  26.  * authorization.                                                           *
  27.  ****************************************************************************/
  28.  
  29. /****************************************************************************
  30.  *  Author: Zeyd M. Ben-Halim <zmbenhal@netcom.com> 1992,1995               *
  31.  *     and: Eric S. Raymond <esr@snark.thyrsus.com>                         *
  32.  ****************************************************************************/
  33.  
  34. /*
  35. **    lib_mvcur.c
  36. **
  37. **    The routines for moving the physical cursor and scrolling:
  38. **
  39. **        void _nc_mvcur_init(void)
  40. **
  41. **        void _nc_mvcur_resume(void)
  42. **
  43. **        int mvcur(int old_y, int old_x, int new_y, int new_x)
  44. **
  45. **        void _nc_mvcur_wrap(void)
  46. **
  47. ** Comparisons with older movement optimizers:
  48. **    SVr3 curses mvcur() can't use cursor_to_ll or auto_left_margin.
  49. **    4.4BSD curses can't use cuu/cud/cuf/cub/hpa/vpa/tab/cbt for local
  50. ** motions.  It doesn't use tactics based on auto_left_margin.  Weirdly
  51. ** enough, it doesn't use its own hardware-scrolling routine to scroll up
  52. ** destination lines for out-of-bounds addresses!
  53. **    old ncurses optimizer: less accurate cost computations (in fact,
  54. ** it was broken and had to be commented out!).
  55. **
  56. ** Compile with -DMAIN to build an interactive tester/timer for the movement
  57. ** optimizer.  You can use it to investigate the optimizer's behavior.
  58. ** You can also use it for tuning the formulas used to determine whether
  59. ** or not full optimization is attempted.
  60. **
  61. ** This code has a nasty tendency to find bugs in terminfo entries, because it
  62. ** exercises the non-cup movement capabilities heavily.  If you think you've
  63. ** found a bug, try deleting subsets of the following capabilities (arranged
  64. ** in decreasing order of suspiciousness): it, tab, cbt, hpa, vpa, cuu, cud,
  65. ** cuf, cub, cuu1, cud1, cuf1, cub1.  It may be that one or more are wrong.
  66. **
  67. ** Note: you should expect this code to look like a resource hog in a profile.
  68. ** That's because it does a lot of I/O, through the tputs() calls.  The I/O
  69. ** cost swamps the computation overhead (and as machines get faster, this
  70. ** will become even more true).  Comments in the test exerciser at the end
  71. ** go into detail about tuning and how you can gauge the optimizer's
  72. ** effectiveness.
  73. **/
  74.  
  75. /****************************************************************************
  76.  *
  77.  * Constants and macros for optimizer tuning.
  78.  *
  79.  ****************************************************************************/
  80.  
  81. /*
  82.  * The average overhead of a full optimization computation in character
  83.  * transmission times.  If it's too high, the algorithm will be a bit
  84.  * over-biased toward using cup rather than local motions; if it's too
  85.  * low, the algorithm may spend more time than is strictly optimal
  86.  * looking for non-cup motions.  Profile the optimizer using the `t'
  87.  * command of the exerciser (see below), and round to the nearest integer.
  88.  *
  89.  * Yes, I (esr) thought about computing expected overhead dynamically, say
  90.  * by derivation from a running average of optimizer times.  But the
  91.  * whole point of this optimization is to *decrease* the frequency of
  92.  * system calls. :-)
  93.  */
  94. #define COMPUTE_OVERHEAD    1    /* I use a 90MHz Pentium @ 9.6Kbps */
  95.  
  96. /*
  97.  * LONG_DIST is the distance we consider to be just as costly to move over as a
  98.  * cup sequence is to emit.  In other words, it's the length of a cup sequence
  99.  * adjusted for average computation overhead.  The magic number is the length
  100.  * of "\033[yy;xxH", the typical cup sequence these days.
  101.  */
  102. #define LONG_DIST        (8 - COMPUTE_OVERHEAD)
  103.  
  104. /*
  105.  * Tell whether a motion is optimizable by local motions.  Needs to be cheap to
  106.  * compute. In general, all the fast moves go to either the right or left edge
  107.  * of the screen.  So any motion to a location that is (a) further away than
  108.  * LONG_DIST and (b) further inward from the right or left edge than LONG_DIST,
  109.  * we'll consider nonlocal.
  110.  */
  111. #define NOT_LOCAL(fy, fx, ty, tx)    ((tx > LONG_DIST) && (tx < screen_lines - 1 - LONG_DIST) && (abs(ty-fy) + abs(tx-fx) > LONG_DIST))
  112.  
  113. /****************************************************************************
  114.  *
  115.  * External interfaces
  116.  *
  117.  ****************************************************************************/
  118.  
  119. /*
  120.  * For this code to work OK, the following components must live in the
  121.  * screen structure:
  122.  *
  123.  *    int        _char_padding;    // cost of character put
  124.  *    int        _cr_cost;    // cost of (carriage_return)
  125.  *    int        _cup_cost;    // cost of (cursor_address)
  126.  *    int        _home_cost;    // cost of (cursor_home)
  127.  *    int        _ll_cost;    // cost of (cursor_to_ll)
  128.  *#if USE_HARD_TABS
  129.  *    int        _ht_cost;    // cost of (tab)
  130.  *    int        _cbt_cost;    // cost of (back_tab)
  131.  *#endif USE_HARD_TABS
  132.  *    int        _cub1_cost;    // cost of (cursor_left)
  133.  *    int        _cuf1_cost;    // cost of (cursor_right)
  134.  *    int        _cud1_cost;    // cost of (cursor_down)
  135.  *    int        _cuu1_cost;    // cost of (cursor_up)
  136.  *    int        _cub_cost;    // cost of (parm_cursor_left)
  137.  *    int        _cuf_cost;    // cost of (parm_cursor_right)
  138.  *    int        _cud_cost;    // cost of (parm_cursor_down)
  139.  *    int        _cuu_cost;    // cost of (parm_cursor_up)
  140.  *    int        _hpa_cost;    // cost of (column_address)
  141.  *    int        _vpa_cost;    // cost of (row_address)
  142.  *    int        _ech_cost;    // cost of (erase_chars)
  143.  *    int        _rep_cost;    // cost of (repeat_char)
  144.  *
  145.  * The USE_HARD_TABS switch controls whether it is reliable to use tab/backtabs
  146.  * for local motions.  On many systems, it's not, due to uncertainties about
  147.  * tab delays and whether or not tabs will be expanded in raw mode.  If you
  148.  * have parm_right_cursor, tab motions don't win you a lot anyhow.
  149.  */
  150.  
  151. #include <curses.priv.h>
  152. #include <term.h>
  153. #include <ctype.h>
  154.  
  155. MODULE_ID("$Id: lib_mvcur.c,v 1.86 2002/09/14 23:02:06 Philippe.Blain Exp $")
  156.  
  157. #define CURRENT_ROW    SP->_cursrow    /* phys cursor row */
  158. #define CURRENT_COLUMN    SP->_curscol    /* phys cursor column */
  159. #define CURRENT_ATTR    SP->_current_attr    /* current phys attribute */
  160. #define REAL_ATTR    SP->_current_attr    /* phys current attribute */
  161. #define WANT_CHAR(y, x)    SP->_newscr->_line[y].text[x]    /* desired state */
  162. #define BAUDRATE    cur_term->_baudrate    /* bits per second */
  163.  
  164. #if defined(MAIN) || defined(NCURSES_TEST)
  165. #include <sys/time.h>
  166.  
  167. static bool profiling = FALSE;
  168. static float diff;
  169. #endif /* MAIN */
  170.  
  171. #define OPT_SIZE 512
  172.  
  173. static int normalized_cost(const char *const cap, int affcnt);
  174.  
  175. /****************************************************************************
  176.  *
  177.  * Initialization/wrapup (including cost pre-computation)
  178.  *
  179.  ****************************************************************************/
  180.  
  181. #ifdef TRACE
  182. static int
  183. trace_cost_of(const char *capname, const char *cap, int affcnt)
  184. {
  185.     int result = _nc_msec_cost(cap, affcnt);
  186.     TR(TRACE_CHARPUT | TRACE_MOVE,
  187.        ("CostOf %s %d %s", capname, result, _nc_visbuf(cap)));
  188.     return result;
  189. }
  190. #define CostOf(cap,affcnt) trace_cost_of(#cap,cap,affcnt);
  191.  
  192. static int
  193. trace_normalized_cost(const char *capname, const char *cap, int affcnt)
  194. {
  195.     int result = normalized_cost(cap, affcnt);
  196.     TR(TRACE_CHARPUT | TRACE_MOVE,
  197.        ("NormalizedCost %s %d %s", capname, result, _nc_visbuf(cap)));
  198.     return result;
  199. }
  200. #define NormalizedCost(cap,affcnt) trace_normalized_cost(#cap,cap,affcnt);
  201.  
  202. #else
  203.  
  204. #define CostOf(cap,affcnt) _nc_msec_cost(cap,affcnt);
  205. #define NormalizedCost(cap,affcnt) normalized_cost(cap,affcnt);
  206.  
  207. #endif
  208.  
  209. NCURSES_EXPORT(int)
  210. _nc_msec_cost(const char *const cap, int affcnt)
  211. /* compute the cost of a given operation */
  212. {
  213.     if (cap == 0)
  214.     return (INFINITY);
  215.     else {
  216.     const char *cp;
  217.     float cum_cost = 0.0;
  218.  
  219.     for (cp = cap; *cp; cp++) {
  220.         /* extract padding, either mandatory or required */
  221.         if (cp[0] == '$' && cp[1] == '<' && strchr(cp, '>')) {
  222.         float number = 0.0;
  223.  
  224.         for (cp += 2; *cp != '>'; cp++) {
  225.             if (isdigit(UChar(*cp)))
  226.             number = number * 10 + (*cp - '0');
  227.             else if (*cp == '*')
  228.             number *= affcnt;
  229.             else if (*cp == '.' && (*++cp != '>') && isdigit(UChar(*cp)))
  230.             number += (*cp - '0') / 10.0;
  231.         }
  232.  
  233. #if NCURSES_NO_PADDING
  234.         if (!(SP->_no_padding))
  235. #endif
  236.             cum_cost += number * 10;
  237.         } else
  238.         cum_cost += SP->_char_padding;
  239.     }
  240.  
  241.     return ((int) cum_cost);
  242.     }
  243. }
  244.  
  245. static int
  246. normalized_cost(const char *const cap, int affcnt)
  247. /* compute the effective character-count for an operation (round up) */
  248. {
  249.     int cost = _nc_msec_cost(cap, affcnt);
  250.     if (cost != INFINITY)
  251.     cost = (cost + SP->_char_padding - 1) / SP->_char_padding;
  252.     return cost;
  253. }
  254.  
  255. static void
  256. reset_scroll_region(void)
  257. /* Set the scroll-region to a known state (the default) */
  258. {
  259.     if (change_scroll_region) {
  260.     TPUTS_TRACE("change_scroll_region");
  261.     putp(tparm(change_scroll_region, 0, screen_lines - 1));
  262.     }
  263. }
  264.  
  265. NCURSES_EXPORT(void)
  266. _nc_mvcur_resume(void)
  267. /* what to do at initialization time and after each shellout */
  268. {
  269.     /* initialize screen for cursor access */
  270.     if (enter_ca_mode) {
  271.     TPUTS_TRACE("enter_ca_mode");
  272.     putp(enter_ca_mode);
  273.     }
  274.  
  275.     /*
  276.      * Doing this here rather than in _nc_mvcur_wrap() ensures that
  277.      * ncurses programs will see a reset scroll region even if a
  278.      * program that messed with it died ungracefully.
  279.      *
  280.      * This also undoes the effects of terminal init strings that assume
  281.      * they know the screen size.  This is useful when you're running
  282.      * a vt100 emulation through xterm.
  283.      */
  284.     reset_scroll_region();
  285.     SP->_cursrow = SP->_curscol = -1;
  286.  
  287.     /* restore cursor shape */
  288.     if (SP->_cursor != -1) {
  289.     int cursor = SP->_cursor;
  290.     SP->_cursor = -1;
  291.     curs_set(cursor);
  292.     }
  293. }
  294.  
  295. NCURSES_EXPORT(void)
  296. _nc_mvcur_init(void)
  297. /* initialize the cost structure */
  298. {
  299.     /*
  300.      * 9 = 7 bits + 1 parity + 1 stop.
  301.      */
  302.     SP->_char_padding = (9 * 1000 * 10) / (BAUDRATE > 0 ? BAUDRATE : 9600);
  303.     if (SP->_char_padding <= 0)
  304.     SP->_char_padding = 1;    /* must be nonzero */
  305.     TR(TRACE_CHARPUT | TRACE_MOVE, ("char_padding %d msecs", SP->_char_padding));
  306.  
  307.     /* non-parameterized local-motion strings */
  308.     SP->_cr_cost = CostOf(carriage_return, 0);
  309.     SP->_home_cost = CostOf(cursor_home, 0);
  310.     SP->_ll_cost = CostOf(cursor_to_ll, 0);
  311. #if USE_HARD_TABS
  312.     SP->_ht_cost = CostOf(tab, 0);
  313.     SP->_cbt_cost = CostOf(back_tab, 0);
  314. #endif /* USE_HARD_TABS */
  315.     SP->_cub1_cost = CostOf(cursor_left, 0);
  316.     SP->_cuf1_cost = CostOf(cursor_right, 0);
  317.     SP->_cud1_cost = CostOf(cursor_down, 0);
  318.     SP->_cuu1_cost = CostOf(cursor_up, 0);
  319.  
  320.     SP->_smir_cost = CostOf(enter_insert_mode, 0);
  321.     SP->_rmir_cost = CostOf(exit_insert_mode, 0);
  322.     SP->_ip_cost = 0;
  323.     if (insert_padding) {
  324.     SP->_ip_cost = CostOf(insert_padding, 0);
  325.     }
  326.  
  327.     /*
  328.      * Assumption: if the terminal has memory_relative addressing, the
  329.      * initialization strings or smcup will set single-page mode so we
  330.      * can treat it like absolute screen addressing.  This seems to be true
  331.      * for all cursor_mem_address terminal types in the terminfo database.
  332.      */
  333.     SP->_address_cursor = cursor_address ? cursor_address : cursor_mem_address;
  334.  
  335.     /*
  336.      * Parametrized local-motion strings.  This static cost computation
  337.      * depends on the following assumptions:
  338.      *
  339.      * (1) They never have * padding.  In the entire master terminfo database
  340.      *     as of March 1995, only the obsolete Zenith Z-100 pc violates this.
  341.      *     (Proportional padding is found mainly in insert, delete and scroll
  342.      *     capabilities).
  343.      *
  344.      * (2) The average case of cup has two two-digit parameters.  Strictly,
  345.      *     the average case for a 24 * 80 screen has ((10*10*(1 + 1)) +
  346.      *     (14*10*(1 + 2)) + (10*70*(2 + 1)) + (14*70*4)) / (24*80) = 3.458
  347.      *     digits of parameters.  On a 25x80 screen the average is 3.6197.
  348.      *     On larger screens the value gets much closer to 4.
  349.      *
  350.      * (3) The average case of cub/cuf/hpa/ech/rep has 2 digits of parameters
  351.      *     (strictly, (((10 * 1) + (70 * 2)) / 80) = 1.8750).
  352.      *
  353.      * (4) The average case of cud/cuu/vpa has 2 digits of parameters
  354.      *     (strictly, (((10 * 1) + (14 * 2)) / 24) = 1.5833).
  355.      *
  356.      * All these averages depend on the assumption that all parameter values
  357.      * are equally probable.
  358.      */
  359.     SP->_cup_cost = CostOf(tparm(SP->_address_cursor, 23, 23), 1);
  360.     SP->_cub_cost = CostOf(tparm(parm_left_cursor, 23), 1);
  361.     SP->_cuf_cost = CostOf(tparm(parm_right_cursor, 23), 1);
  362.     SP->_cud_cost = CostOf(tparm(parm_down_cursor, 23), 1);
  363.     SP->_cuu_cost = CostOf(tparm(parm_up_cursor, 23), 1);
  364.     SP->_hpa_cost = CostOf(tparm(column_address, 23), 1);
  365.     SP->_vpa_cost = CostOf(tparm(row_address, 23), 1);
  366.  
  367.     /* non-parameterized screen-update strings */
  368.     SP->_ed_cost = NormalizedCost(clr_eos, 1);
  369.     SP->_el_cost = NormalizedCost(clr_eol, 1);
  370.     SP->_el1_cost = NormalizedCost(clr_bol, 1);
  371.     SP->_dch1_cost = NormalizedCost(delete_character, 1);
  372.     SP->_ich1_cost = NormalizedCost(insert_character, 1);
  373.  
  374.     /* parameterized screen-update strings */
  375.     SP->_dch_cost = NormalizedCost(tparm(parm_dch, 23), 1);
  376.     SP->_ich_cost = NormalizedCost(tparm(parm_ich, 23), 1);
  377.     SP->_ech_cost = NormalizedCost(tparm(erase_chars, 23), 1);
  378.     SP->_rep_cost = NormalizedCost(tparm(repeat_char, ' ', 23), 1);
  379.  
  380.     SP->_cup_ch_cost = NormalizedCost(tparm(SP->_address_cursor, 23, 23), 1);
  381.     SP->_hpa_ch_cost = NormalizedCost(tparm(column_address, 23), 1);
  382.     SP->_cuf_ch_cost = NormalizedCost(tparm(parm_right_cursor, 23), 1);
  383.     SP->_inline_cost = min(SP->_cup_ch_cost,
  384.                min(SP->_hpa_ch_cost,
  385.                    SP->_cuf_ch_cost));
  386.  
  387.     /*
  388.      * If save_cursor is used within enter_ca_mode, we should not use it for
  389.      * scrolling optimization, since the corresponding restore_cursor is not
  390.      * nested on the various terminals (vt100, xterm, etc.) which use this
  391.      * feature.
  392.      */
  393.     if (save_cursor != 0
  394.     && enter_ca_mode != 0
  395.     && strstr(enter_ca_mode, save_cursor) != 0) {
  396.     T(("...suppressed sc/rc capability due to conflict with smcup/rmcup"));
  397.     save_cursor = 0;
  398.     restore_cursor = 0;
  399.     }
  400.  
  401.     /*
  402.      * A different, possibly better way to arrange this would be to set
  403.      * SP->_endwin = TRUE at window initialization time and let this be
  404.      * called by doupdate's return-from-shellout code.
  405.      */
  406.     _nc_mvcur_resume();
  407. }
  408.  
  409. NCURSES_EXPORT(void)
  410. _nc_mvcur_wrap(void)
  411. /* wrap up cursor-addressing mode */
  412. {
  413.     /* leave cursor at screen bottom */
  414.     mvcur(-1, -1, screen_lines - 1, 0);
  415.  
  416.     /* set cursor to normal mode */
  417.     if (SP->_cursor != -1)
  418.     curs_set(1);
  419.  
  420.     if (exit_ca_mode) {
  421.     TPUTS_TRACE("exit_ca_mode");
  422.     putp(exit_ca_mode);
  423.     }
  424.     /*
  425.      * Reset terminal's tab counter.  There's a long-time bug that
  426.      * if you exit a "curses" program such as vi or more, tab
  427.      * forward, and then backspace, the cursor doesn't go to the
  428.      * right place.  The problem is that the kernel counts the
  429.      * escape sequences that reset things as column positions.
  430.      * Utter a \r to reset this invisibly.
  431.      */
  432.     _nc_outch('\r');
  433. }
  434.  
  435. /****************************************************************************
  436.  *
  437.  * Optimized cursor movement
  438.  *
  439.  ****************************************************************************/
  440.  
  441. /*
  442.  * Perform repeated-append, returning cost
  443.  */
  444. static inline int
  445. repeated_append(string_desc * target, int total, int num, int repeat, const char *src)
  446. {
  447.     size_t need = repeat * strlen(src);
  448.  
  449.     if (need < target->s_size) {
  450.     while (repeat-- > 0) {
  451.         if (_nc_safe_strcat(target, src)) {
  452.         total += num;
  453.         } else {
  454.         total = INFINITY;
  455.         break;
  456.         }
  457.     }
  458.     } else {
  459.     total = INFINITY;
  460.     }
  461.     return total;
  462. }
  463.  
  464. #ifndef NO_OPTIMIZE
  465. #define NEXTTAB(fr)    (fr + init_tabs - (fr % init_tabs))
  466.  
  467. /*
  468.  * Assume back_tab (CBT) does not wrap backwards at the left margin, return
  469.  * a negative value at that point to simplify the loop.
  470.  */
  471. #define LASTTAB(fr)    ((fr > 0) ? ((fr - 1) / init_tabs) * init_tabs : -1)
  472.  
  473. static int
  474. relative_move(string_desc * target, int from_y, int from_x, int to_y, int
  475.           to_x, bool ovw)
  476. /* move via local motions (cuu/cuu1/cud/cud1/cub1/cub/cuf1/cuf/vpa/hpa) */
  477. {
  478.     string_desc save;
  479.     int n, vcost = 0, hcost = 0;
  480.  
  481.     (void) _nc_str_copy(&save, target);
  482.  
  483.     if (to_y != from_y) {
  484.     vcost = INFINITY;
  485.  
  486.     if (row_address != 0
  487.         && _nc_safe_strcat(target, tparm(row_address, to_y))) {
  488.         vcost = SP->_vpa_cost;
  489.     }
  490.  
  491.     if (to_y > from_y) {
  492.         n = (to_y - from_y);
  493.  
  494.         if (parm_down_cursor
  495.         && SP->_cud_cost < vcost
  496.         && _nc_safe_strcat(_nc_str_copy(target, &save),
  497.                    tparm(parm_down_cursor, n))) {
  498.         vcost = SP->_cud_cost;
  499.         }
  500.  
  501.         if (cursor_down
  502.         && (*cursor_down != '\n' || SP->_nl)
  503.         && (n * SP->_cud1_cost < vcost)) {
  504.         vcost = repeated_append(_nc_str_copy(target, &save), 0,
  505.                     SP->_cud1_cost, n, cursor_down);
  506.         }
  507.     } else {        /* (to_y < from_y) */
  508.         n = (from_y - to_y);
  509.  
  510.         if (parm_up_cursor
  511.         && SP->_cup_cost < vcost
  512.         && _nc_safe_strcat(_nc_str_copy(target, &save),
  513.                    tparm(parm_up_cursor, n))) {
  514.         vcost = SP->_cup_cost;
  515.         }
  516.  
  517.         if (cursor_up && (n * SP->_cuu1_cost < vcost)) {
  518.         vcost = repeated_append(_nc_str_copy(target, &save), 0,
  519.                     SP->_cuu1_cost, n, cursor_up);
  520.         }
  521.     }
  522.  
  523.     if (vcost == INFINITY)
  524.         return (INFINITY);
  525.     }
  526.  
  527.     save = *target;
  528.  
  529.     if (to_x != from_x) {
  530.     char str[OPT_SIZE];
  531.     string_desc check;
  532.  
  533.     hcost = INFINITY;
  534.  
  535.     if (column_address
  536.         && _nc_safe_strcat(_nc_str_copy(target, &save),
  537.                    tparm(column_address, to_x))) {
  538.         hcost = SP->_hpa_cost;
  539.     }
  540.  
  541.     if (to_x > from_x) {
  542.         n = to_x - from_x;
  543.  
  544.         if (parm_right_cursor
  545.         && SP->_cuf_cost < hcost
  546.         && _nc_safe_strcat(_nc_str_copy(target, &save),
  547.                    tparm(parm_right_cursor, n))) {
  548.         hcost = SP->_cuf_cost;
  549.         }
  550.  
  551.         if (cursor_right) {
  552.         int lhcost = 0;
  553.  
  554.         (void) _nc_str_init(&check, str, sizeof(str));
  555.  
  556. #if USE_HARD_TABS
  557.         /* use hard tabs, if we have them, to do as much as possible */
  558.         if (init_tabs > 0 && tab) {
  559.             int nxt, fr;
  560.  
  561.             for (fr = from_x; (nxt = NEXTTAB(fr)) <= to_x; fr = nxt) {
  562.             lhcost = repeated_append(&check, lhcost,
  563.                          SP->_ht_cost, 1, tab);
  564.             if (lhcost == INFINITY)
  565.                 break;
  566.             }
  567.  
  568.             n = to_x - fr;
  569.             from_x = fr;
  570.         }
  571. #endif /* USE_HARD_TABS */
  572.  
  573. #if defined(REAL_ATTR) && defined(WANT_CHAR)
  574.         if (n <= 0 || n >= (int) check.s_size)
  575.             ovw = FALSE;
  576. #if BSD_TPUTS
  577.         /*
  578.          * If we're allowing BSD-style padding in tputs, don't generate
  579.          * a string with a leading digit.  Otherwise, that will be
  580.          * interpreted as a padding value rather than sent to the
  581.          * screen.
  582.          */
  583.         if (ovw
  584.             && n > 0
  585.             && n < (int) check.s_size
  586.             && vcost == 0
  587.             && str[0] == '\0'
  588.             && isdigit(CharOf(WANT_CHAR(to_y, from_x))))
  589.             ovw = FALSE;
  590. #endif
  591.         /*
  592.          * If we have no attribute changes, overwrite is cheaper.
  593.          * Note: must suppress this by passing in ovw = FALSE whenever
  594.          * WANT_CHAR would return invalid data.  In particular, this
  595.          * is true between the time a hardware scroll has been done
  596.          * and the time the structure WANT_CHAR would access has been
  597.          * updated.
  598.          */
  599.         if (ovw) {
  600.             int i;
  601.  
  602.             for (i = 0; i < n; i++) {
  603.             NCURSES_CH_T ch = WANT_CHAR(to_y, from_x + i);
  604.             if (AttrOf(ch) != CURRENT_ATTR
  605. #if USE_WIDEC_SUPPORT
  606.                 || !Charable(ch)
  607. #endif
  608.                 ) {
  609.                 ovw = FALSE;
  610.                 break;
  611.             }
  612.             }
  613.         }
  614.         if (ovw) {
  615.             int i;
  616.  
  617.             for (i = 0; i < n; i++)
  618.             *check.s_tail++ = CharOf(WANT_CHAR(to_y, from_x + i));
  619.             *check.s_tail = '\0';
  620.             check.s_size -= n;
  621.             lhcost += n * SP->_char_padding;
  622.         } else
  623. #endif /* defined(REAL_ATTR) && defined(WANT_CHAR) */
  624.         {
  625.             lhcost = repeated_append(&check, lhcost, SP->_cuf1_cost,
  626.                          n, cursor_right);
  627.         }
  628.  
  629.         if (lhcost < hcost
  630.             && _nc_safe_strcat(_nc_str_copy(target, &save), str)) {
  631.             hcost = lhcost;
  632.         }
  633.         }
  634.     } else {        /* (to_x < from_x) */
  635.         n = from_x - to_x;
  636.  
  637.         if (parm_left_cursor
  638.         && SP->_cub_cost < hcost
  639.         && _nc_safe_strcat(_nc_str_copy(target, &save),
  640.                    tparm(parm_left_cursor, n))) {
  641.         hcost = SP->_cub_cost;
  642.         }
  643.  
  644.         if (cursor_left) {
  645.         int lhcost = 0;
  646.  
  647.         (void) _nc_str_init(&check, str, sizeof(str));
  648.  
  649. #if USE_HARD_TABS
  650.         if (init_tabs > 0 && back_tab) {
  651.             int nxt, fr;
  652.  
  653.             for (fr = from_x; (nxt = LASTTAB(fr)) >= to_x; fr = nxt) {
  654.             lhcost = repeated_append(&check, lhcost,
  655.                          SP->_cbt_cost, 1, back_tab);
  656.             if (lhcost == INFINITY)
  657.                 break;
  658.             }
  659.  
  660.             n = fr - to_x;
  661.         }
  662. #endif /* USE_HARD_TABS */
  663.  
  664.         lhcost = repeated_append(&check, lhcost, SP->_cub1_cost, n, cursor_left);
  665.  
  666.         if (lhcost < hcost
  667.             && _nc_safe_strcat(_nc_str_copy(target, &save), str)) {
  668.             hcost = lhcost;
  669.         }
  670.         }
  671.     }
  672.  
  673.     if (hcost == INFINITY)
  674.         return (INFINITY);
  675.     }
  676.  
  677.     return (vcost + hcost);
  678. }
  679. #endif /* !NO_OPTIMIZE */
  680.  
  681. /*
  682.  * With the machinery set up above, it's conceivable that
  683.  * onscreen_mvcur could be modified into a recursive function that does
  684.  * an alpha-beta search of motion space, as though it were a chess
  685.  * move tree, with the weight function being boolean and the search
  686.  * depth equated to length of string.  However, this would jack up the
  687.  * computation cost a lot, especially on terminals without a cup
  688.  * capability constraining the search tree depth.  So we settle for
  689.  * the simpler method below.
  690.  */
  691.  
  692. static inline int
  693. onscreen_mvcur(int yold, int xold, int ynew, int xnew, bool ovw)
  694. /* onscreen move from (yold, xold) to (ynew, xnew) */
  695. {
  696.     string_desc result;
  697.     char buffer[OPT_SIZE];
  698.     int tactic = 0, newcost, usecost = INFINITY;
  699.     int t5_cr_cost;
  700.  
  701. #if defined(MAIN) || defined(NCURSES_TEST)
  702.     struct timeval before, after;
  703.  
  704.     gettimeofday(&before, NULL);
  705. #endif /* MAIN */
  706.  
  707. #define NullResult _nc_str_null(&result, sizeof(buffer))
  708. #define InitResult _nc_str_init(&result, buffer, sizeof(buffer))
  709.  
  710.     /* tactic #0: use direct cursor addressing */
  711.     if (_nc_safe_strcpy(InitResult, tparm(SP->_address_cursor, ynew, xnew))) {
  712.     tactic = 0;
  713.     usecost = SP->_cup_cost;
  714.  
  715. #if defined(TRACE) || defined(NCURSES_TEST)
  716.     if (!(_nc_optimize_enable & OPTIMIZE_MVCUR))
  717.         goto nonlocal;
  718. #endif /* TRACE */
  719.  
  720.     /*
  721.      * We may be able to tell in advance that the full optimization
  722.      * will probably not be worth its overhead.  Also, don't try to
  723.      * use local movement if the current attribute is anything but
  724.      * A_NORMAL...there are just too many ways this can screw up
  725.      * (like, say, local-movement \n getting mapped to some obscure
  726.      * character because A_ALTCHARSET is on).
  727.      */
  728.     if (yold == -1 || xold == -1 || NOT_LOCAL(yold, xold, ynew, xnew)) {
  729. #if defined(MAIN) || defined(NCURSES_TEST)
  730.         if (!profiling) {
  731.         (void) fputs("nonlocal\n", stderr);
  732.         goto nonlocal;    /* always run the optimizer if profiling */
  733.         }
  734. #else
  735.         goto nonlocal;
  736. #endif /* MAIN */
  737.     }
  738.     }
  739. #ifndef NO_OPTIMIZE
  740.     /* tactic #1: use local movement */
  741.     if (yold != -1 && xold != -1
  742.     && ((newcost = relative_move(NullResult, yold, xold, ynew, xnew,
  743.                      ovw)) != INFINITY)
  744.     && newcost < usecost) {
  745.     tactic = 1;
  746.     usecost = newcost;
  747.     }
  748.  
  749.     /* tactic #2: use carriage-return + local movement */
  750.     if (yold != -1 && carriage_return
  751.     && ((newcost = relative_move(NullResult, yold, 0, ynew, xnew, ovw))
  752.         != INFINITY)
  753.     && SP->_cr_cost + newcost < usecost) {
  754.     tactic = 2;
  755.     usecost = SP->_cr_cost + newcost;
  756.     }
  757.  
  758.     /* tactic #3: use home-cursor + local movement */
  759.     if (cursor_home
  760.     && ((newcost = relative_move(NullResult, 0, 0, ynew, xnew, ovw)) != INFINITY)
  761.     && SP->_home_cost + newcost < usecost) {
  762.     tactic = 3;
  763.     usecost = SP->_home_cost + newcost;
  764.     }
  765.  
  766.     /* tactic #4: use home-down + local movement */
  767.     if (cursor_to_ll
  768.     && ((newcost = relative_move(NullResult, screen_lines - 1, 0, ynew,
  769.                      xnew, ovw)) != INFINITY)
  770.     && SP->_ll_cost + newcost < usecost) {
  771.     tactic = 4;
  772.     usecost = SP->_ll_cost + newcost;
  773.     }
  774.  
  775.     /*
  776.      * tactic #5: use left margin for wrap to right-hand side,
  777.      * unless strange wrap behavior indicated by xenl might hose us.
  778.      */
  779.     t5_cr_cost = (xold > 0 ? SP->_cr_cost : 0);
  780.     if (auto_left_margin && !eat_newline_glitch
  781.     && yold > 0 && cursor_left
  782.     && ((newcost = relative_move(NullResult, yold - 1, screen_columns -
  783.                      1, ynew, xnew, ovw)) != INFINITY)
  784.     && t5_cr_cost + SP->_cub1_cost + newcost < usecost) {
  785.     tactic = 5;
  786.     usecost = t5_cr_cost + SP->_cub1_cost + newcost;
  787.     }
  788.  
  789.     /*
  790.      * These cases are ordered by estimated relative frequency.
  791.      */
  792.     if (tactic)
  793.     InitResult;
  794.     switch (tactic) {
  795.     case 1:
  796.     (void) relative_move(&result, yold, xold, ynew, xnew, ovw);
  797.     break;
  798.     case 2:
  799.     (void) _nc_safe_strcpy(&result, carriage_return);
  800.     (void) relative_move(&result, yold, 0, ynew, xnew, ovw);
  801.     break;
  802.     case 3:
  803.     (void) _nc_safe_strcpy(&result, cursor_home);
  804.     (void) relative_move(&result, 0, 0, ynew, xnew, ovw);
  805.     break;
  806.     case 4:
  807.     (void) _nc_safe_strcpy(&result, cursor_to_ll);
  808.     (void) relative_move(&result, screen_lines - 1, 0, ynew, xnew, ovw);
  809.     break;
  810.     case 5:
  811.     if (xold > 0)
  812.         (void) _nc_safe_strcat(&result, carriage_return);
  813.     (void) _nc_safe_strcat(&result, cursor_left);
  814.     (void) relative_move(&result, yold - 1, screen_columns - 1, ynew,
  815.                  xnew, ovw);
  816.     break;
  817.     }
  818. #endif /* !NO_OPTIMIZE */
  819.  
  820. #if defined(MAIN) || defined(NCURSES_TEST)
  821.     gettimeofday(&after, NULL);
  822.     diff = after.tv_usec - before.tv_usec
  823.     + (after.tv_sec - before.tv_sec) * 1000000;
  824.     if (!profiling)
  825.     (void) fprintf(stderr,
  826.                "onscreen: %d msec, %f 28.8Kbps char-equivalents\n",
  827.                (int) diff, diff / 288);
  828. #endif /* MAIN */
  829.  
  830.   nonlocal:
  831.     if (usecost != INFINITY) {
  832.     TPUTS_TRACE("mvcur");
  833.     tputs(buffer, 1, _nc_outch);
  834.     return (OK);
  835.     } else
  836.     return (ERR);
  837. }
  838.  
  839. NCURSES_EXPORT(int)
  840. mvcur(int yold, int xold, int ynew, int xnew)
  841. /* optimized cursor move from (yold, xold) to (ynew, xnew) */
  842. {
  843.     TR(TRACE_CALLS | TRACE_MOVE, (T_CALLED("mvcur(%d,%d,%d,%d)"),
  844.                   yold, xold, ynew, xnew));
  845.  
  846.     if (SP == 0)
  847.     returnCode(ERR);
  848.  
  849.     if (yold == ynew && xold == xnew)
  850.     returnCode(OK);
  851.  
  852.     /*
  853.      * Most work here is rounding for terminal boundaries getting the
  854.      * column position implied by wraparound or the lack thereof and
  855.      * rolling up the screen to get ynew on the screen.
  856.      */
  857.  
  858.     if (xnew >= screen_columns) {
  859.     ynew += xnew / screen_columns;
  860.     xnew %= screen_columns;
  861.     }
  862.     if (xold >= screen_columns) {
  863.     int l;
  864.  
  865.     if (SP->_nl) {
  866.         l = (xold + 1) / screen_columns;
  867.         yold += l;
  868.         if (yold >= screen_lines)
  869.         l -= (yold - screen_lines - 1);
  870.  
  871.         while (l > 0) {
  872.         if (newline) {
  873.             TPUTS_TRACE("newline");
  874.             tputs(newline, 0, _nc_outch);
  875.         } else
  876.             putchar('\n');
  877.         l--;
  878.         if (xold > 0) {
  879.             if (carriage_return) {
  880.             TPUTS_TRACE("carriage_return");
  881.             tputs(carriage_return, 0, _nc_outch);
  882.             } else
  883.             putchar('\r');
  884.             xold = 0;
  885.         }
  886.         }
  887.     } else {
  888.         /*
  889.          * If caller set nonl(), we cannot really use newlines to position
  890.          * to the next row.
  891.          */
  892.         xold = -1;
  893.         yold = -1;
  894.     }
  895.     }
  896.  
  897.     if (yold > screen_lines - 1)
  898.     yold = screen_lines - 1;
  899.     if (ynew > screen_lines - 1)
  900.     ynew = screen_lines - 1;
  901.  
  902.     /* destination location is on screen now */
  903.     returnCode(onscreen_mvcur(yold, xold, ynew, xnew, TRUE));
  904. }
  905.  
  906. #if defined(TRACE) || defined(NCURSES_TEST)
  907. NCURSES_EXPORT_VAR(int) _nc_optimize_enable = OPTIMIZE_ALL;
  908. #endif
  909.  
  910. #if defined(MAIN) || defined(NCURSES_TEST)
  911. /****************************************************************************
  912.  *
  913.  * Movement optimizer test code
  914.  *
  915.  ****************************************************************************/
  916.  
  917. #include <tic.h>
  918. #include <dump_entry.h>
  919.  
  920. NCURSES_EXPORT_VAR(const char *) _nc_progname = "mvcur";
  921.  
  922. static unsigned long xmits;
  923.  
  924. /* these override lib_tputs.c */
  925. NCURSES_EXPORT(int)
  926. tputs(const char *string, int affcnt GCC_UNUSED, int (*outc) (int) GCC_UNUSED)
  927. /* stub tputs() that dumps sequences in a visible form */
  928. {
  929.     if (profiling)
  930.     xmits += strlen(string);
  931.     else
  932.     (void) fputs(_nc_visbuf(string), stdout);
  933.     return (OK);
  934. }
  935.  
  936. NCURSES_EXPORT(int)
  937. putp(const char *string)
  938. {
  939.     return (tputs(string, 1, _nc_outch));
  940. }
  941.  
  942. NCURSES_EXPORT(int)
  943. _nc_outch(int ch)
  944. {
  945.     putc(ch, stdout);
  946.     return OK;
  947. }
  948.  
  949. NCURSES_EXPORT(int)
  950. delay_output(int ms GCC_UNUSED)
  951. {
  952.     return OK;
  953. }
  954.  
  955. static char tname[MAX_ALIAS];
  956.  
  957. static void
  958. load_term(void)
  959. {
  960.     (void) setupterm(tname, STDOUT_FILENO, NULL);
  961. }
  962.  
  963. static int
  964. roll(int n)
  965. {
  966.     int i, j;
  967.  
  968.     i = (RAND_MAX / n) * n;
  969.     while ((j = rand()) >= i)
  970.     continue;
  971.     return (j % n);
  972. }
  973.  
  974. int
  975. main(int argc GCC_UNUSED, char *argv[]GCC_UNUSED)
  976. {
  977.     (void) strcpy(tname, termname());
  978.     load_term();
  979.     _nc_setupscreen(lines, columns, stdout);
  980.     baudrate();
  981.  
  982.     _nc_mvcur_init();
  983.     NC_BUFFERED(FALSE);
  984.  
  985.     (void) puts("The mvcur tester.  Type ? for help");
  986.  
  987.     fputs("smcup:", stdout);
  988.     putchar('\n');
  989.  
  990.     for (;;) {
  991.     int fy, fx, ty, tx, n, i;
  992.     char buf[BUFSIZ], capname[BUFSIZ];
  993.  
  994.     (void) fputs("> ", stdout);
  995.     (void) fgets(buf, sizeof(buf), stdin);
  996.  
  997.     if (buf[0] == '?') {
  998.         (void) puts("?                -- display this help message");
  999.         (void)
  1000.         puts("fy fx ty tx      -- (4 numbers) display (fy,fx)->(ty,tx) move");
  1001.         (void) puts("s[croll] n t b m -- display scrolling sequence");
  1002.         (void)
  1003.         printf("r[eload]         -- reload terminal info for %s\n",
  1004.                termname());
  1005.         (void)
  1006.         puts("l[oad] <term>    -- load terminal info for type <term>");
  1007.         (void) puts("d[elete] <cap>   -- delete named capability");
  1008.         (void) puts("i[nspect]        -- display terminal capabilities");
  1009.         (void)
  1010.         puts("c[ost]           -- dump cursor-optimization cost table");
  1011.         (void) puts("o[optimize]      -- toggle movement optimization");
  1012.         (void)
  1013.         puts("t[orture] <num>  -- torture-test with <num> random moves");
  1014.         (void) puts("q[uit]           -- quit the program");
  1015.     } else if (sscanf(buf, "%d %d %d %d", &fy, &fx, &ty, &tx) == 4) {
  1016.         struct timeval before, after;
  1017.  
  1018.         putchar('"');
  1019.  
  1020.         gettimeofday(&before, NULL);
  1021.         mvcur(fy, fx, ty, tx);
  1022.         gettimeofday(&after, NULL);
  1023.  
  1024.         printf("\" (%ld msec)\n",
  1025.            (long) (after.tv_usec - before.tv_usec
  1026.                + (after.tv_sec - before.tv_sec)
  1027.                * 1000000));
  1028.     } else if (sscanf(buf, "s %d %d %d %d", &fy, &fx, &ty, &tx) == 4) {
  1029.         struct timeval before, after;
  1030.  
  1031.         putchar('"');
  1032.  
  1033.         gettimeofday(&before, NULL);
  1034.         _nc_scrolln(fy, fx, ty, tx);
  1035.         gettimeofday(&after, NULL);
  1036.  
  1037.         printf("\" (%ld msec)\n",
  1038.            (long) (after.tv_usec - before.tv_usec + (after.tv_sec -
  1039.                                  before.tv_sec)
  1040.                * 1000000));
  1041.     } else if (buf[0] == 'r') {
  1042.         (void) strcpy(tname, termname());
  1043.         load_term();
  1044.     } else if (sscanf(buf, "l %s", tname) == 1) {
  1045.         load_term();
  1046.     } else if (sscanf(buf, "d %s", capname) == 1) {
  1047.         struct name_table_entry const *np = _nc_find_entry(capname,
  1048.                                    _nc_info_hash_table);
  1049.  
  1050.         if (np == NULL)
  1051.         (void) printf("No such capability as \"%s\"\n", capname);
  1052.         else {
  1053.         switch (np->nte_type) {
  1054.         case BOOLEAN:
  1055.             cur_term->type.Booleans[np->nte_index] = FALSE;
  1056.             (void)
  1057.             printf("Boolean capability `%s' (%d) turned off.\n",
  1058.                    np->nte_name, np->nte_index);
  1059.             break;
  1060.  
  1061.         case NUMBER:
  1062.             cur_term->type.Numbers[np->nte_index] = ABSENT_NUMERIC;
  1063.             (void) printf("Number capability `%s' (%d) set to -1.\n",
  1064.                   np->nte_name, np->nte_index);
  1065.             break;
  1066.  
  1067.         case STRING:
  1068.             cur_term->type.Strings[np->nte_index] = ABSENT_STRING;
  1069.             (void) printf("String capability `%s' (%d) deleted.\n",
  1070.                   np->nte_name, np->nte_index);
  1071.             break;
  1072.         }
  1073.         }
  1074.     } else if (buf[0] == 'i') {
  1075.         dump_init((char *) NULL, F_TERMINFO, S_TERMINFO, 70, 0, FALSE);
  1076.         dump_entry(&cur_term->type, FALSE, TRUE, 0, 0, 0);
  1077.         putchar('\n');
  1078.     } else if (buf[0] == 'o') {
  1079.         if (_nc_optimize_enable & OPTIMIZE_MVCUR) {
  1080.         _nc_optimize_enable &= ~OPTIMIZE_MVCUR;
  1081.         (void) puts("Optimization is now off.");
  1082.         } else {
  1083.         _nc_optimize_enable |= OPTIMIZE_MVCUR;
  1084.         (void) puts("Optimization is now on.");
  1085.         }
  1086.     }
  1087.     /*
  1088.      * You can use the `t' test to profile and tune the movement
  1089.      * optimizer.  Use iteration values in three digits or more.
  1090.      * At above 5000 iterations the profile timing averages are stable
  1091.      * to within a millisecond or three.
  1092.      *
  1093.      * The `overhead' field of the report will help you pick a
  1094.      * COMPUTE_OVERHEAD figure appropriate for your processor and
  1095.      * expected line speed.  The `total estimated time' is
  1096.      * computation time plus a character-transmission time
  1097.      * estimate computed from the number of transmits and the baud
  1098.      * rate.
  1099.      *
  1100.      * Use this together with the `o' command to get a read on the
  1101.      * optimizer's effectiveness.  Compare the total estimated times
  1102.      * for `t' runs of the same length in both optimized and un-optimized
  1103.      * modes.  As long as the optimized times are less, the optimizer
  1104.      * is winning.
  1105.      */
  1106.     else if (sscanf(buf, "t %d", &n) == 1) {
  1107.         float cumtime = 0.0, perchar;
  1108.         int speeds[] =
  1109.         {2400, 9600, 14400, 19200, 28800, 38400, 0};
  1110.  
  1111.         srand((unsigned) (getpid() + time((time_t *) 0)));
  1112.         profiling = TRUE;
  1113.         xmits = 0;
  1114.         for (i = 0; i < n; i++) {
  1115.         /*
  1116.          * This does a move test between two random locations,
  1117.          * Random moves probably short-change the optimizer,
  1118.          * which will work better on the short moves probably
  1119.          * typical of doupdate()'s usage pattern.  Still,
  1120.          * until we have better data...
  1121.          */
  1122. #ifdef FIND_COREDUMP
  1123.         int from_y = roll(lines);
  1124.         int to_y = roll(lines);
  1125.         int from_x = roll(columns);
  1126.         int to_x = roll(columns);
  1127.  
  1128.         printf("(%d,%d) -> (%d,%d)\n", from_y, from_x, to_y, to_x);
  1129.         mvcur(from_y, from_x, to_y, to_x);
  1130. #else
  1131.         mvcur(roll(lines), roll(columns), roll(lines), roll(columns));
  1132. #endif /* FIND_COREDUMP */
  1133.         if (diff)
  1134.             cumtime += diff;
  1135.         }
  1136.         profiling = FALSE;
  1137.  
  1138.         /*
  1139.          * Average milliseconds per character optimization time.
  1140.          * This is the key figure to watch when tuning the optimizer.
  1141.          */
  1142.         perchar = cumtime / n;
  1143.  
  1144.         (void) printf("%d moves (%ld chars) in %d msec, %f msec each:\n",
  1145.               n, xmits, (int) cumtime, perchar);
  1146.  
  1147.         for (i = 0; speeds[i]; i++) {
  1148.         /*
  1149.          * Total estimated time for the moves, computation and
  1150.          * transmission both. Transmission time is an estimate
  1151.          * assuming 9 bits/char, 8 bits + 1 stop bit.
  1152.          */
  1153.         float totalest = cumtime + xmits * 9 * 1e6 / speeds[i];
  1154.  
  1155.         /*
  1156.          * Per-character optimization overhead in character transmits
  1157.          * at the current speed.  Round this to the nearest integer
  1158.          * to figure COMPUTE_OVERHEAD for the speed.
  1159.          */
  1160.         float overhead = speeds[i] * perchar / 1e6;
  1161.  
  1162.         (void)
  1163.             printf("%6d bps: %3.2f char-xmits overhead; total estimated time %15.2f\n",
  1164.                speeds[i], overhead, totalest);
  1165.         }
  1166.     } else if (buf[0] == 'c') {
  1167.         (void) printf("char padding: %d\n", SP->_char_padding);
  1168.         (void) printf("cr cost: %d\n", SP->_cr_cost);
  1169.         (void) printf("cup cost: %d\n", SP->_cup_cost);
  1170.         (void) printf("home cost: %d\n", SP->_home_cost);
  1171.         (void) printf("ll cost: %d\n", SP->_ll_cost);
  1172. #if USE_HARD_TABS
  1173.         (void) printf("ht cost: %d\n", SP->_ht_cost);
  1174.         (void) printf("cbt cost: %d\n", SP->_cbt_cost);
  1175. #endif /* USE_HARD_TABS */
  1176.         (void) printf("cub1 cost: %d\n", SP->_cub1_cost);
  1177.         (void) printf("cuf1 cost: %d\n", SP->_cuf1_cost);
  1178.         (void) printf("cud1 cost: %d\n", SP->_cud1_cost);
  1179.         (void) printf("cuu1 cost: %d\n", SP->_cuu1_cost);
  1180.         (void) printf("cub cost: %d\n", SP->_cub_cost);
  1181.         (void) printf("cuf cost: %d\n", SP->_cuf_cost);
  1182.         (void) printf("cud cost: %d\n", SP->_cud_cost);
  1183.         (void) printf("cuu cost: %d\n", SP->_cuu_cost);
  1184.         (void) printf("hpa cost: %d\n", SP->_hpa_cost);
  1185.         (void) printf("vpa cost: %d\n", SP->_vpa_cost);
  1186.     } else if (buf[0] == 'x' || buf[0] == 'q')
  1187.         break;
  1188.     else
  1189.         (void) puts("Invalid command.");
  1190.     }
  1191.  
  1192.     (void) fputs("rmcup:", stdout);
  1193.     _nc_mvcur_wrap();
  1194.     putchar('\n');
  1195.  
  1196.     return (0);
  1197. }
  1198.  
  1199. #endif /* MAIN */
  1200.  
  1201. /* lib_mvcur.c ends here */
  1202.